package org.streets.database.datadict.impl;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import org.slf4j.Logger;
import org.streets.commons.util.ClassLoaderUtils;
import org.streets.commons.util.ClassUtils;
import org.streets.context.ResourceLocator;
import org.streets.context.ResourceLocator.FetchType;
import org.streets.database.EntityPackageManager;
import org.streets.database.annotations.DataDictDef;
import org.streets.database.datadict.ColumnDefinition;
import org.streets.database.datadict.DataDict;
import org.streets.database.datadict.TableDefinition;

@SuppressWarnings("serial")
public class DataDictImpl implements DataDict, Serializable {
	
	private List<TableDefinition> tables = new ArrayList<TableDefinition>();
	
	public DataDictImpl(Logger logger, EntityPackageManager packageManager, ResourceLocator locator) {
		
		ClassLoader classLoader = ClassLoaderUtils.getDefaultClassLoader();
		
        for (String p_n : packageManager.getNames()) {
        	
        	Collection<Object> mappings = locator.locateResources(p_n, "class", FetchType.NAME);
        	
        	Class<?> entityClazz = null;
        	
        	for (Object mapping : mappings) {
        		
        		try {
        			entityClazz = classLoader.loadClass((String)mapping);
				} catch (ClassNotFoundException ex) {					
					logger.error("Can not find Entity ["+entityClazz+"]", ex);
				}
				
				if (entityClazz == null) continue;
				
				Table table = entityClazz.getAnnotation(Table.class);
    			DataDictDef t_def = entityClazz.getAnnotation(DataDictDef.class);

				if (t_def == null || table == null) continue;
				
				TableDefinition td = new TableDefinitionImpl(table, t_def);
				
				Field[] fields = ClassUtils.getDeclaredFields(entityClazz);					
				for (Field field : fields) {
					
					Id id = field.getAnnotation(Id.class);					
					Column column = field.getAnnotation(Column.class);					
					DataDictDef f_def = field.getAnnotation(DataDictDef.class);
					
					if ((id == null && column == null) || f_def == null) continue;
					
					Class<?> f_type = field.getType();
					String f_name = field.getName();					
					ColumnDefinition cd = new ColumnDefinitionImpl(td, id, column, f_def, f_name, f_type);						
					td.getColumns().add(cd);
				}
				// add table definition to dictionary
				tables.add(td);
				logger.info("-> Entity ["+entityClazz+"] added to dictionay");
        	}
        }		
	}

	public List<TableDefinition> getTableDefs() {
		return tables;
	}
	
	public TableDefinition getTableDef(String name) {
		
		TableDefinition result = null;
		for (TableDefinition def : tables) {
			if (def.getName().equalsIgnoreCase(name)) {
				result = def;
				break;
			}			
		}
		return result;
	}
}
